Skip to content

native_lib/trace: fix and reenable #4435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 9, 2025
Merged

Conversation

nia-e
Copy link
Contributor

@nia-e nia-e commented Jul 2, 2025

Per discussion in various PRs (#4401, #4405), this addresses (some of) the points raised. Also using this to check if CI passes on all architectures.

@nia-e nia-e force-pushed the trace-fixups branch 4 times, most recently from 37fbac4 to d2ed49d Compare July 2, 2025 03:27
@nia-e
Copy link
Contributor Author

nia-e commented Jul 2, 2025

The only options I see for making the builds work nicely regardless of arch are either (a) copy-pasting a massive cfg macro everywhere; (b) copy-pasting said massive macro only a couple times to declare some stub functions; or (c) give in and add a small build script. Given the size of Miri I assume the choice to not have said build script was very deliberate, so I can try to do one of the other options (or maybe I'm missing something!) but for now this hopefully will build. This would have solved everything trivially, but oh well

@nia-e nia-e force-pushed the trace-fixups branch 2 times, most recently from 0110f5a to 6907e9d Compare July 2, 2025 03:58
@RalfJung
Copy link
Member

RalfJung commented Jul 2, 2025

It should be entirely possible to do this with a single cfg that decides whether we include the "real" implementation of native_lib::tracing, or a fallback.

//#[cfg(target_os = "linux")]
//pub mod trace;
#[cfg(trace)]
pub mod trace;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To reduce the amount of cfg in this file, I would suggest something like this:

#[cfg_attr(not(all(target_os = "linux", any(target_arch = "x86_64", ...), path = "trace/stub.rs")]
mod trace;

and then the stub.rs file contains just a bunch of empty functions and dummy type aliases so that to the outside, it looks like all the same functions and types exist.

src/alloc/mod.rs Outdated
@@ -1,5 +1,5 @@
mod alloc_bytes;
#[cfg(target_os = "linux")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just keep using that allocator on all Linux targets, no harm in that.

@RalfJung
Copy link
Member

RalfJung commented Jul 5, 2025

@rustbot author

@rustbot rustbot added the S-waiting-on-author Status: Waiting for the PR author to address review comments label Jul 5, 2025
@rustbot
Copy link
Collaborator

rustbot commented Jul 5, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@nia-e nia-e force-pushed the trace-fixups branch 2 times, most recently from 3b1584e to 93015d6 Compare July 5, 2025 10:09
@nia-e nia-e force-pushed the trace-fixups branch 4 times, most recently from 9e54dab to f2eb0e3 Compare July 5, 2025 11:39
@nia-e nia-e force-pushed the trace-fixups branch 3 times, most recently from 60837c7 to f411f4f Compare July 5, 2025 12:29
@nia-e nia-e force-pushed the trace-fixups branch 3 times, most recently from 2f028a6 to 9e8e277 Compare July 5, 2025 12:48
@nia-e
Copy link
Contributor Author

nia-e commented Jul 5, 2025

Seems like CI is fine.

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Waiting for a review to complete and removed S-waiting-on-author Status: Waiting for the PR author to address review comments labels Jul 5, 2025
@nia-e nia-e changed the title Fixes for trace code native-lib/trace: fix and reenable Jul 5, 2025
@nia-e nia-e changed the title native-lib/trace: fix and reenable native_lib/trace: fix and reenable Jul 5, 2025
Comment on lines 56 to 58
// This might be desired behaviour, as even on platforms where ptracing
// is not implemented it enables us to enforce that only one FFI call
// happens at a time.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not very reassuring if the code says "might be desired behavior".^^

Suggested change
// This might be desired behaviour, as even on platforms where ptracing
// is not implemented it enables us to enforce that only one FFI call
// happens at a time.
// As a side-effect, even on platforms where ptracing
// is not implemented, we enforce that only one FFI call
// happens at a time.

It's kind of odd though that we would do this for "Linux without ptrace", but not do it on macos...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outside of many-seeds performance-sensitive scenarios, I find it hard to imagine when we wouldn't be okay with this blocking. I do recall running into a concrete case where this was desired; the ptr_write_access test will write to statics, so performing it in in many-seeds mode without the tracing caused it to randomly segfault (but it was fine with, as it forced the threads to take turns)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm fine with the blocking.

But then the do_ffi implementation in trace/stub.rs should also do it to ensure feature parity. :)

const WAIT_FLAGS: wait::WaitPidFlag =
wait::WaitPidFlag::from_bits_truncate(libc::WUNTRACED | libc::WEXITED);
wait::WaitPidFlag::WUNTRACED.union(wait::WaitPidFlag::WEXITED);

/// Arch-specific maximum size a single access might perform. x86 value is set
/// assuming nothing bigger than AVX-512 is available.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
const ARCH_MAX_ACCESS_SIZE: usize = 64;
/// The largest arm64 simd instruction operates on 16 bytes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any citation or so you can give for this?
Seems pretty important that we don't get this wrong.^^

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add a comment explaining the rationale, but there isn't really a canonical set of flags to use for this that I know of. WEXITED makes sure we actually kill the child process when it exits instead of leaving it as a zombie, and WUNTRACED is mostly helpful at the very start before we've ptraced

Copy link
Member

@RalfJung RalfJung Jul 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, I think you misunderstood my comment.^^ It refers to this:

The largest arm64 simd instruction operates on 16 bytes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh! oops. Yes, I'll add a citation

@nia-e nia-e force-pushed the trace-fixups branch 5 times, most recently from ef187be to 199bf13 Compare July 5, 2025 18:49
@nia-e nia-e force-pushed the trace-fixups branch 2 times, most recently from a06efe1 to f17c0ce Compare July 6, 2025 07:39
@RalfJung
Copy link
Member

RalfJung commented Jul 7, 2025

@rustbot ready
(I know this is redundant but I need something in my inbox to represent this ;)

Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have rebased and pushed a commit with some cleanups, please check if those make sense.

@@ -417,71 +412,13 @@ fn handle_segfault(
if acc_ty.is_writable() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it actually happen that the same operation is both a read and a write? Is that used for instance for atomic fetch-and-add and operations like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know which ops exactly (I know on arm64 there's an explicit swap register with memory op for example), but yes there are x86 ops that do both. The write isn't always done on all ops; per iced which I was using before and is explicitly x86-focused so I'll trust as a source, the possible options are Read, CondRead, Write, CondWrite, ReadWrite, and ReadCondWrite. Since this only triggers when an access occurs, we can treat CondRead and CondWrite the same as their normal versions bc we know the condition was met, and putting a write after the read on a ReadCondWrite if the condition wasn't met is still fine in the sense that we don't lose information, just might accidentally mark uninit stuff as init.

We could maybe get around this with another mempr_* function so that instead we first set perms to read instead of RW, and if it retriggers a segfault we know for sure the write happens, but that might add a good bit of code complexity. It's an idea I've had for a bit though and plan on implementing at some point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh so these events are still approximate, we don't know if both a read and a write actually happened? Good to know, we have to keep that in mind when interpreting the events. That reduces the benefit of having precise range information...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's only on the RCW events. I can add a boolean is_certain field on the write and if it's false we double-check that the allocation it's writing to is mutable first, but that will be a very rare edge case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is UB to run compare_exchange on an immutable allocation even if it ends up not writing anything. So that particular check should be fine, if it only affects such atomic operations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll pour over the iced source later then, just to see which instructions that flag is set for. Ty!

@nia-e
Copy link
Contributor Author

nia-e commented Jul 9, 2025

They all make sense, yeah. Figured I could leave the correct bits of the arm64 code as long as they're disabled, but I'll just add them all back at once in a later PR ^^

@RalfJung
Copy link
Member

RalfJung commented Jul 9, 2025

They all make sense, yeah. Figured I could leave the correct bits of the arm64 code as long as they're disabled, but I'll just add them all back at once in a later PR ^^

I'd rather not have code in the repo that doesn't even get built.

@RalfJung RalfJung enabled auto-merge July 9, 2025 09:03
Co-Authored-By: Ralf Jung <post@ralfj.de>
@RalfJung RalfJung added this pull request to the merge queue Jul 9, 2025
Merged via the queue into rust-lang:master with commit ffa564d Jul 9, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Waiting for a review to complete
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants